#include "token.h"
#include "preprocessor.h"
#include "parse_history.h"
#include <string.h>
#include <map.h>

int32 dec_num[256];
int32 oct_num[256];
int32 hex_num[256];
int32 id_header[256];
int32 id_body[256];
int32 space[256];
smap *keyword;

void pp_init()
{
    static const char* word[] = {
        "auto","break","case","char","const","continue","default",
        "do","double","else","enum","extern","float","for","goto",
        "if","inline","int","long","register","restrict","return",
        "short","signed","sizeof","static","struct","switch","typedef",
        "union","unsigned","void","volatile","while","_BOOL","_COMPLEX",
        "_IMAGINARY",
    };
    static int32 flag[] = {
        KW_AUTO,KW_BREAK,KW_CASE,KW_CHAR,KW_CONST,KW_CONTINUE,KW_DEFAULT,
        KW_DO,KW_DOUBLE,KW_ELSE,KW_ENUM,KW_EXTERN,KW_FLOAT,KW_FOR,KW_GOTO,
        KW_IF,KW_INLINE,KW_INT,KW_LONG,KW_REGISTER,KW_RESTRICT,KW_RETURN,
        KW_SHORT,KW_SIGNED,KW_SIZEOF,KW_STATIC,KW_STRUCT,KW_SWITCH,KW_TYPEDEF,
        KW_UNION,KW_UNSIGNED,KW_VOID,KW_VOLATILE,KW_WHILE,KW_BOOL,KW_COMPLEX,
        KW_IMAGINARY,
    };
    int i;
    memset( dec_num, 0, sizeof(dec_num) );
    memset( oct_num, 0, sizeof(oct_num) );
    memset( hex_num, 0, sizeof(hex_num) );
    memset( id_header, 0, sizeof(id_header) );
    memset( id_body, 0, sizeof(id_body) );
    memset( space, 0, sizeof(space) );

    for( i = '0'; i <= '9'; ++i )
        dec_num[i] = 1;
    for( i = '0'; i <= '7'; ++i )
        oct_num[i] = 1;
    for( i = '0'; i <= '9'; ++i )
        hex_num[i] = 1;
    for( i = 'A'; i <= 'F'; ++i )
        hex_num[i] = 1;
    for( i = 'a'; i <= 'f'; ++i )
        hex_num[i] = 1;
    for( i = 'A'; i <= 'Z'; ++i )
        id_header[i] = 1;
    for( i = 'a'; i <= 'z'; ++i )
        id_header[i] = 1;
    id_header['_'] = 1;
    id_header['$'] = 1;
    memcpy( id_body, id_header, sizeof(id_body) );
    for( i = '0'; i <= '9'; ++i )
        id_body[i] = 1;

    space[' '] = 1;
    space['\t'] = 1;
    space['\r'] = 1;
    space['\n'] = 2;

    keyword = sm_create();
    for( i = 0; i < sizeof(word)/sizeof(word[0]); ++i )
        sm_insert( keyword, word[i], (void*)flag[i] );
}

int32 pp_identify( code* cd, ppoutput *out )
{
    uint8* ptr;
    token* tk;
    int flag;
    ptr = cd->code_buf;
    if( id_header[*ptr] )
    {
        while( id_body[*++ptr] );
        tk= token_create( IDENTIFIER, cd, ptr, NULL, (exp_tree*)lt_back( out->ppcond) );
        if( 0 == sm_find( keyword, tk->name, (void**)&flag ) )
            tk->type = KEYWORD | flag;
        lt_pushb( out->tokens, tk );
        cd->code_buf = ptr;
        return 0;
    }
    return -1;
}

int32 pp_universal( code* cd, ppoutput *out ); // decide to drop this feature

int32 pp_integer( code* cd, ppoutput *out )
{
    uint8* ptr;
    token* tk;
    int32 flag = INTEGER;

    ptr = cd->code_buf;
    if( *ptr == '0' )
    {
        if( *++ptr == 'x' || *ptr == 'X' )  // hex
            while( hex_num[*++ptr] != 0 );
        else if( oct_num[*ptr] != 0 )    // oct
            while( oct_num[*++ptr] != 0 );
    }
    else if( dec_num[*ptr] != 0 )   // dec
        while( dec_num[*++ptr] != 0 );
    else
        return -1;
    if( *ptr == 'u' || *ptr == 'U' )
    {
        ++ptr;
        if( *ptr == 'l' || *ptr == 'L' )
            ++ptr;
        if( *ptr == 'l' || *ptr == 'L' )
            ++ptr;
    }
    else if ( *ptr == 'l' || *ptr == 'L' )
    {
        ++ptr;
        if( *ptr == 'l' || *ptr == 'L' )
            ++ptr;
        if( *ptr == 'u' || *ptr == 'U' )
            ++ptr;
    }

    tk = token_create( INTEGER, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
    lt_pushb( out->tokens, tk );
    cd->code_buf = ptr;
    return 0;
}


int32 pp_floating( code* cd, ppoutput *out )
{
    uint8* ptr;
    token *tk;
    int32 is_floating = 0, num = 0;

    ptr = cd->code_buf;
    if( ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X') )
    {
        ptr += 2;
        if( !hex_num[*ptr] )
            return -1;
        while( hex_num[*++ptr] != 0 ) ;
        if( *ptr == '.' )
            while( hex_num[*++ptr] != 0 ) ;
        if( *ptr != 'p' && *ptr++ != 'P' )
            return -1;
        if( *ptr == '+' || *ptr == '-' )
            ++ptr;
        if( !dec_num[*ptr] )
            return -1;
        while( dec_num[*++ptr] != 0 );
        if( *ptr == 'l' || *ptr == 'L' )
            ++ptr;
        if( *ptr == 'f' || *ptr == 'F' )
            ++ptr;
        tk = token_create( FLOATING, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
        lt_pushb( out->tokens, tk );
        cd->code_buf = ptr;
        return 0;
    }
    else
    {
        if( dec_num[*ptr] != 0 )
            num = 1;
        while( dec_num[*ptr] != 0 )
            ++ptr;
        if( *ptr == '.' )
        {
            ++ptr;
            is_floating = 1;
            if( dec_num[*ptr] != 0 )
                num = 1;
            while( dec_num[*ptr] != 0 )
                ++ptr;
        }
        // is not a number
        if( ptr == cd->code_buf || !num )
            return -1;

        if( *ptr == 'e' || *ptr == 'E' )
        {
            ++ptr;
            is_floating = 1;
            if( *ptr == '-' || *ptr == '+' )
                ++ptr;
            while( dec_num[*++ptr] != 0 ) ;
        }
        if( *ptr == 'l' || *ptr == 'L' )
            ++ptr;
        if( *ptr == 'f' || *ptr == 'F' )
            ++ptr;
        if( is_floating == 0 )
            return -1;

        tk = token_create( FLOATING, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
        lt_pushb( out->tokens, tk );
        cd->code_buf = ptr;
        return 0;
    }
}

int32 pp_character( code* cd, ppoutput *out )
{
    uint8* ptr;
    token* tk;
    ptr = cd->code_buf;

    if( *ptr == '\'' )
        ++ptr;
    else if( ptr[0] == 'L' && ptr[1] == '\'' )
        ptr += 2;
    else
        return -1;
    while ( *ptr != '\'' && *ptr != '\n' && *ptr != 0 )
    {
        if( *ptr == '\\' )
        {
            ++ptr;
            if( oct_num[*ptr] != 0 )
            {
                ++ptr;
                if( oct_num[*ptr] != 0 )
                    ++ptr;
                if( oct_num[*ptr] != 0 )
                    ++ptr;
            }
            else if( *ptr == 'x' || *ptr == 'X' )
            {
                ++ptr;
                if( hex_num[*ptr] != 0 )
                    while( hex_num[*++ptr] != 0 ) ;
            }
            else
            {
                ++ptr;
            }
        }
        else
        {
            ++ptr;
        }
    }
    if( *ptr == '\'' )
        ++ptr;
    tk = token_create( STRING, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
    lt_pushb( out->tokens, tk );
    cd->code_buf = ptr;
    return 0;
}

int32 pp_string( code* cd, ppoutput *out )
{
    uint8* ptr;
    token* tk;
    ptr = cd->code_buf;

    if( *ptr == '\"' )
        ++ptr;
    else if( ptr[0] == 'L' && ptr[1] == '\'' )
        ptr += 2;
    else
        return -1;
    while ( *ptr != '\"' && *ptr != '\n' && *ptr != 0 )
    {
        if( *ptr == '\\' )
        {
            ++ptr;
            if( oct_num[*ptr] != 0 )
            {
                ++ptr;
                if( oct_num[*ptr] != 0 )
                    ++ptr;
                if( oct_num[*ptr] != 0 )
                    ++ptr;
            }
            else if( *ptr == 'x' || *ptr == 'X' )
            {
                ++ptr;
                if( hex_num[*ptr] != 0 )
                    while( hex_num[*++ptr] != 0 ) ;
            }
            else
            {
                ++ptr;
            }
        }
        else
        {
            ++ptr;
        }
    }
    if( *ptr == '\"' )
        ++ptr;
    tk = token_create( STRING, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
    lt_pushb( out->tokens, tk );
    cd->code_buf = ptr;
    return 0;
}

int32 pp_punctuator( code* cd, ppoutput *out )
{
    uint8* ptr;
    token* tk;
    int32 flag = OPERATOR;
    ptr = cd->code_buf;

    switch( *ptr )
    {
    case '.':
        if( ptr[1] == '.' && ptr[2] == '.' )
        {
            ptr += 3;
        }
        else
        {
            ++ptr;
            flag |= PCT_DOT;
        }
        break;
    case '<':
        if( ptr[1] == '<' && ptr[2] == '=' )
        {
            ptr += 3;
            flag |= PCT_ASSIGN_SHIFT_L;
        }
        else if( ptr[1] == '<' )
        {
            ptr += 2;
            flag |= PCT_SHIFT_L;
        }
        else if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_SMALL_EQUAL;
        }
        else
        {
            ++ptr;
            flag |= PCT_SMALL;
        }
        break;
    case '>':
        if( ptr[1] == '>' && ptr[2] == '=' )
        {
            ptr += 3;
            flag |= PCT_ASSIGN_SHIFT_R;
        }
        else if( ptr[1] == '>' )
        {
            ptr += 2;
            flag += PCT_SHIFT_R;
        }
        else if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_LARGE_EQUAL;
        }
        else
        {
            ++ptr;
            flag |= PCT_LARGE;
        }
        break;
    case '*':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_ASSIGN_MUL;
        }
        else
        {
            ++ptr;
            flag |= PCT_MUL;
        }
        break;
    case '/':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_ASSIGN_DEV;
        }
        else
        {
            ++ptr;
            flag |= PCT_DEV;
        }
        break;
    case '%':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_ASSIGN_MOD;
        }
        else
        {
            ++ptr;
            flag |= PCT_MOD;
        }
        break;
    case '+':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_ASSIGN_ADD;
        }
        else if( ptr[1] == '+' )
        {
            ptr += 2;
            flag |= PCT_INC_POST;
        }
        else
        {
            ++ptr;
            flag |= PCT_ADD;
        }
        break;
    case '-':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_ASSIGN_SUB;
        }
        else if( ptr[1] == '>' )
        {
            ptr += 2;
            flag |= PCT_POINT;
        }
        else if( ptr[1] == '-' )
        {
            ptr += 2;
            flag |= PCT_DEC_POST;
        }
        else
        {
            ++ptr;
            flag |= PCT_SUB;
        }
        break;
    case '&':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_ASSIGN_AND;
        }
        else if( ptr[1] == '&' )
        {
            ptr += 2;
            flag |= PCT_LOGICAL_AND;
        }
        else
        {
            ++ptr;
            flag |= PCT_AND;
        }
        break;
    case '^':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_ASSIGN_XOR;
        }
        else
        {
            ++ptr;
            flag |= PCT_EXCLUSIVE_OR;
        }
        break;
    case '|':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_ASSIGN_OR;
        }
        else
        {
            ++ptr;
            flag |= PCT_INCLUSIVE_OR;
        }
        break;
    case '=':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_EQUAL;
        }
        else
        {
            ++ptr;
            flag |= PCT_ASSIGN;
        }
        break;
    case '!':
        if( ptr[1] == '=' )
        {
            ptr += 2;
            flag |= PCT_UNEQUAL;
        }
        else
        {
            ++ptr;
            flag |= PCT_LOGICAL_NOT;
        }
        break;
    case '#':
        if( ptr[1] == '#' )
            ptr += 2;
        else
            ++ptr;
        break;
    case '[':
        ++ptr;
        flag |= PCT_ARRAY_L;
        break;
    case ']':
        ++ptr;
        flag |= PCT_ARRAY_R;
        break;
    case '(':
        ++ptr;
        flag |= PCT_PARENTHESES_L;
        break;
    case ')':
        ++ptr;
        flag |= PCT_PARENTHESES_R;
        break;
    case '?':
    case ':':
        ++ptr;
        flag |= PCT_CONDITION;
        break;
    case '~':
        ++ptr;
        flag |= PCT_NOT;
        break;
    case ',':
        ++ptr;
        flag |= PCT_COMMA;
        break;
    case '{':
    case '}':
    case ';':
        ++ptr;
        break;
    default:
        return -1;
    }
    tk = token_create( flag, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
    lt_pushb( out->tokens, tk );
    cd->code_buf = ptr;
    return 0;
}

// we do not use this function
int32 pp_header_name( code* cd, ppoutput *out )
{
    uint8* ptr;
    token* tk;
    uint8 sc, ec;
    ptr = cd->code_buf;

    if( *ptr == '"' )
        ++ptr, sc = ec = '"';
    else if( *ptr == '<' )
        ++ptr, sc = '<', ec = '>';
    else
        return -1;
    while ( *ptr != ec && *ptr != '\n' && *ptr != 0 )
    {
        ++ptr;
    }
    if( *ptr == ec )
        ++ptr;
    tk = token_create( 0, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
    lt_pushb( out->tokens, tk );
    cd->code_buf = ptr;
    return 0;
}

int32 pp_number( code* ci ); // decide to drop this feature

int32 pp_comment( code* cd, ppoutput *out )
{
    token* tk;
    uint8* ptr;
    ptr = cd->code_buf;

    if( ptr[1] == '/' && ptr[0] == '/' )
    {
        ptr += 2;
        while( *ptr != '\n' && *ptr != 0 ) 
            ++ptr;
        tk = token_create( COMMENT, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
        lt_pushb( out->comments, tk );
        cd->code_buf = ptr;
        return 0;
    }
    else if( ptr[1] == '*' && ptr[0] == '/' )
    {
        ptr += 2;
        while( *ptr != 0 && (ptr[0] != '*' || ptr[1] != '/') ) 
            ++ptr;
        if( *ptr != 0 )
            ptr += 2;
        tk = token_create( COMMENT, cd, ptr, NULL, (exp_tree*)lt_back(out->ppcond) );
        lt_pushb( out->comments, tk );
        cd->code_buf = ptr;
        return 0;
    }
    return -1;
}

int32 pp_space( code* cd, ppoutput *out )
{
    uint8* ptr;
    ptr = cd->code_buf;
    while( space[*ptr] != 0 )
        cd->line += space[*ptr++] - 1;
    if( ptr != cd->code_buf )
    {
        cd->code_buf = ptr;
        return 0;
    }
    return -1;
}

int32 pp_if( code* cd, ppoutput *out )
{
    uint8 *ptr;
    ptr = cd->code_buf;
    if( *ptr == '#' )
    {
        if( strncmp( (char*)ptr, "#ifdef", 6 ) == 0 )
        {
            cd->code_buf += 6;
            if( 0 == pp_space( cd, out ) && 0 == pp_identify( cd, out ) )
            {
                token *tk = (token*)lt_popb( out->tokens );
                vartype *vt = vt_find( "int" );
                exp_tree *e = exp_tree_create( vt, (token*)-1, tk, NULL, NULL );
                lt_pushb( out->ppcond, e );
                token_release( tk );
                vt_release( vt );
                return 0;
            }
        }
        else if( strncmp( (char*)ptr, "#ifndef", 7 ) == 0 )
        {
            cd->code_buf += 7;
            if( 0 == pp_identify( cd, out ) )
            {
                token *tk = (token*)lt_popb( out->tokens );
                vartype *vt = vt_find( "int" );
                exp_tree *e = exp_tree_create( vt, (token*)-2, tk, NULL, NULL );
                lt_pushb( out->ppcond, e );
                token_release( tk );
                vt_release( vt );
                return 0;
            }
        }
        else if( strncmp( (char*)ptr, "#if", 3 ) == 0 )
        {
            cd->code_buf += 3;
            if( 0 == pp_space( cd, out ) )
            {
                list *lt = out->tokens;
                lex_info lex;
                exp_tree *e;
                int i;
                int line = cd->line;
                out->tokens = lt_create();
                while( *cd->code_buf && line == cd->line )
                    pp_token( cd, out );
                lex_init( &lex, out->tokens, out->comments, out->errors, out->symbols );
                e = exp_const( &lex );
                lt_pushb( out->ppcond, e );
                for( i = 0; i < lt_size( out->tokens ); ++i )
                    token_release( (token *)lt_get( out->tokens, i ) );
                lt_destroy( out->tokens );
                out->tokens = lt;
                return 0;
            }
        }
        else if( strncmp( (char*)ptr, "#elif", 5 ) == 0 )
        {
            cd->code_buf += 5;
            if( 0 == pp_space( cd, out ) )
            {
                list *lt = out->tokens;
                lex_info lex;
                exp_tree *e;
                int i;
                int line = cd->line;
                out->tokens = lt_create();
                while( *cd->code_buf && line == cd->line )
                    pp_token( cd, out );
                lex_init( &lex, out->tokens, out->comments, out->errors, out->symbols );
                e = exp_const( &lex );
                exp_tree_release( (exp_tree *)lt_popb( out->ppcond ) );
                lt_pushb( out->ppcond, e );
                for( i = 0; i < lt_size( out->tokens ); ++i )
                    token_release( (token *)lt_get( out->tokens, i ) );
                lt_destroy( out->tokens );
                out->tokens = lt;
                return 0;
            }
        }
        else if( strncmp( (char*)ptr, "#else", 5 ) == 0 )
        {
            token *tk;
            exp_tree *e;
            vartype *vt = vt_find( "int" ) ;
            tk = token_create( IDENTIFIER, cd, ptr+5, NULL, (exp_tree*)lt_back(out->ppcond) );
            cd->code_buf += 5;
            while( *cd->code_buf && *cd->code_buf++ != '\n' );
            exp_tree_release( (exp_tree *)lt_popb( out->ppcond ) );
            e = exp_tree_create( vt, (token*)-1, tk, NULL, NULL );
            lt_pushb( out->ppcond, e );
            token_release( tk );
            vt_release( vt );
            return 0;
        }
        else if( strncmp( (char*)ptr, "#endif", 6 ) == 0 )
        {
            cd->code_buf += 6;
            while( *cd->code_buf++ != '\n' );
            exp_tree_release( (exp_tree *)lt_popb( out->ppcond ) );
            return 0;
        }
    }
    return -1;
}

int32 pp_define( code* cd, ppoutput *out )
{
    uint8 *ptr = cd->code_buf;
    macrotype *macro;
    symtype *st;
    symbol *sym;
    token *id;
    if( strncmp( (char*)ptr, "#define", 7 ) == 0 )
    {
        cd->code_buf += 7;
        if( pp_space( cd, out ) == 0 && pp_identify( cd, out ) == 0 )
        {
            id = (token *)lt_popb( out->tokens );
            if( *cd->code_buf == '(' )
            {
                list *lt = out->tokens;
                list *para = lt_create();
                int line = cd->line;
                out->tokens = para;
                ++cd->code_buf;
                while( *cd->code_buf && line == cd->line && *cd->code_buf != ')' )
                {
                    if( 0 == pp_identify( cd, out ) )
                    {
                        pp_space( cd, out );
                        if( *cd->code_buf == ',' )
                        {
                            ++cd->code_buf;
                            pp_space( cd, out );
                        }
                        else
                        {
                            break;
                        }
                    }
                    else if( 0 == pp_punctuator( cd, out ) )
                    {
                        token *tk = (token *)lt_popb( out->tokens );
                        if( 0 != strcmp( tk->name, "..." ) )
                        {
                            token_release( tk );
                            break;
                        }
                        token_release( tk );
                        pp_space( cd, out );
                    }
                }
                if( *cd->code_buf == ')' )
                    ++cd->code_buf;
                out->tokens = lt_create();
                while( *cd->code_buf && line == cd->line )
                    pp_token( cd, out );
                macro = macro_create( id, para, out->tokens );
                st = symtype_create( T_MACRO, macro, 0 );
                sym = sym_create( st, id, NULL );
                symtype_release( st );
                sym_release( sym );
                out->tokens = lt;
            }
            else
            {
                list *lt = out->tokens;
                int line = cd->line;
                out->tokens = lt_create();
                while( *cd->code_buf && line == cd->line )
                    pp_token( cd, out );
                macro = macro_create( id, NULL, out->tokens );
                st = symtype_create( T_MACRO, macro, 0 );
                sym = sym_create( st, id, NULL );
                symtype_release( st );
                sym_release( sym );
                out->tokens = lt;
            }
            token_release( id );
            return 0;
        }
    }
    return -1;
}

int32 pp_other_directive( code* cd, ppoutput *out )
{
    uint8* ptr;

    ptr = cd->code_buf;
    if( *ptr == '#' )
    {
        while( *++ptr != '\n' );
        cd->code_buf = ptr;
        return 0;
    }
    return -1;
}

int32 pp_token( code* cd, ppoutput *out )
{
    while( *cd->code_buf )
    {
        if( 0 == pp_identify( cd, out ) )
        {
            token *tk = (token *)lt_back( out->tokens );
            symbol *sym = sym_find( tk->name );
            if( sym && sym->type->syn == T_MACRO )
            {
                macrotype *mac = (macrotype *)sym->type->attr;
                tk = (token *)lt_popb( out->tokens );
                token_release( tk );
                if( mac->npara == 0 )
                {
                    macro_replace( mac, NULL, out->tokens );
                }
                else
                {
                    list *lt = out->tokens;
                    list *para = lt_create();
                    int npara = 0;
                    out->tokens = para;
                    if( *cd->code_buf == '(' )
                        ++cd->code_buf;
                    while( *cd->code_buf  && *cd->code_buf != ')' && *cd->code_buf != ';' )
                    {
                        if( 0 == pp_token( cd, out ) )
                        {
                            // consider macro parameter( which has problem )
                            tk = (token *)lt_back( out->tokens );
                            npara = lt_size( out->tokens );
                            if( npara-1 <= mac->npara && TOKEN_TYPE(tk) == OPERATOR )
                            {
                                tk = (token *)lt_popb( out->tokens );
                                token_release( tk );
                            }
                        }
                    }
                    if( *cd->code_buf == ')' )
                        ++cd->code_buf;
                    out->tokens = lt;
                    macro_replace( mac, para, out->tokens );
                    lt_destroy( para );
                }
            }

            break;
        }
        else if( 0 == pp_space( cd, out ) )
            break;
        else if( 0 == pp_floating( cd, out ) )
            break;
        else if( 0 == pp_integer( cd, out ) )
            break;
        else if( 0 == pp_comment( cd, out ) )
            break;
        else if( 0 == pp_punctuator( cd, out ) )
            break;
        else if( 0 == pp_string( cd, out ) )
            break;
        else if( 0 == pp_character( cd, out ) )
            break;
        else
        {
            while( *cd->code_buf && space[*cd->code_buf] == 0 )
                ++cd->code_buf;
        }
    }
    return 0;
}

int32 preprocess( code* cd, ppoutput *out )
{
    pp_init();
    sym_init( out->symbols, out->types );
    init_history();
    while( *cd->code_buf )
    {
        if( 0 == pp_if( cd, out ) )
            continue;
        else if( 0 == pp_define( cd, out ) )
            continue;
        else if( 0 == pp_other_directive( cd, out ) )
            continue;
        else
            pp_token( cd, out );
    }
    return 0;
}
